# coding:utf-8

import time
import wx.lib.rcsizer as rcs
import Config
import Global

try:
    from agw import fourwaysplitter as FWS
except ImportError:  # if it's not there locally, try the wxPython lib.
    try:
        import wx.lib.agw.fourwaysplitter as FWS
    except ImportError:
        exit()

# 菜单ID
from AppIds import *

import wx.lib.newevent

# Frame start
SERIAL_GENIUS_FS_CHAR = 0x0F
# Slash
SERIAL_GENIUS_SL_CHAR = 0x1F


# 超级终端
class Terminal(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, style=wx.BORDER_DOUBLE)

        ############# 状态信息 ############
        # 该模块是否启用
        self.isUsing = False

        ############# 配置信息 ############
        self.sendLineBreak = False
        self.ShowCharset = 0
        self.UnshowChars = []
        self.UnsentBuffer = []
        self.AllowChars = []
        self.AllowChars.extend([10, 13])  # \r \n
        self.AllowChars.extend([8])  # backspace
        self.AllowChars.extend([4])  # Ctrl-D
        self.AllowChars.extend(range(32, 127))

        ############# 开始布局 ###########
        gridSizer = rcs.RowColSizer()

        rowTotal = 3
        colTotal = 4

        # 标题
        title = wx.StaticText(self, -1, u"超级终端")
        title.SetFont(wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))
        gridSizer.Add(title, pos=(0, 0), size=(1, colTotal), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)

        # 变量显示区域
        self.dataTerminal = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        # caret = wx.Caret(self,1,100)
        # self.dataTerminal.SetCaret(caret)
        self.dataTerminal.SetFont(wx.Font(10, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        self.dataTerminal.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvents)
        self.dataTerminal.Bind(wx.EVT_CHAR, self.OnChar)
        gridSizer.Add(self.dataTerminal, pos=(1, 0), size=(1, colTotal), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)

        # 清空
        btn = wx.Button(self, -1, u"清空")
        self.Bind(wx.EVT_BUTTON, self.OnClearData, btn)
        gridSizer.Add(btn, pos=(2, 0), size=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL)

        # 保存
        btn = wx.Button(self, -1, u"保存数据")
        self.Bind(wx.EVT_BUTTON, self.OnSaveData, btn)
        gridSizer.Add(btn, pos=(2, 1), size=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL)

        # 显示模式
        ck = wx.CheckBox(self, -1, u"同时发送换行符")
        ck.Bind(wx.EVT_CHECKBOX, self.OnSendLineBreak)
        gridSizer.Add(ck, pos=(2, 2), size=(1, 1), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
        if Config.APP_CONFIG['module_terminal']['sendNewLine'] == '1':
            ck.SetValue(True)
            self.sendLineBreak = True

        # 字符编码
        charsets = [u'ASCII编码', u'UTF8编码', u'GBK编码']
        comboBox = wx.ComboBox(self, -1, choices=charsets, style=wx.CB_READONLY | wx.CB_DROPDOWN)
        gridSizer.Add(comboBox, pos=(2, 3), size=(1, 1), flag=wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
        comboBox.Bind(wx.EVT_COMBOBOX, self.OnCharsetChange)
        comboBox.SetStringSelection(charsets[0])
        if Config.APP_CONFIG['module_terminal']['charset'] == '0':
            comboBox.SetSelection(0)
            self.ShowCharset = 0
        elif Config.APP_CONFIG['module_terminal']['charset'] == '1':
            comboBox.SetSelection(1)
            self.ShowCharset = 1
        elif Config.APP_CONFIG['module_terminal']['charset'] == '2':
            comboBox.SetSelection(2)
            self.ShowCharset = 2

        # 设置变宽信息
        gridSizer.AddGrowableCol(2)
        gridSizer.AddGrowableRow(1)

        ############# 结束布局 ###########
        self.SetSizer(gridSizer)
        self.SetAutoLayout(True)

        # 更新GUI数据的线程
        (self.UpdateDataEvent, self.UPDATA_DATA_EVENT) = wx.lib.newevent.NewEvent()
        self.Bind(self.UPDATA_DATA_EVENT, self.SaveUpdateWindow)

    def OnChar(self, event):
        if event.m_keyCode not in self.AllowChars:
            # print(event.m_keyCode)
            return

        if event.m_keyCode not in [13, 8]:
            self.UnsentBuffer.append(event.m_keyCode)

        if event.m_keyCode == 4:
            self.dataTerminal.write('^')
        elif event.m_keyCode == 8:
            self.UnsentBuffer.pop()

        if Global.SerialGeniusPort and Global.SerialGeniusPort.running:
            event.Skip()
            if event.m_keyCode == 13:
                Global.SerialGeniusPort.SendAppend(self.UnsentBuffer)
                self.UnsentBuffer = []
                if self.sendLineBreak:
                    Global.SerialGeniusPort.SendAppend([0x0D])
                    Global.SerialGeniusPort.SendAppend([0x0A])
        else:
            wx.MessageBox(u'请先打开串口', u'提示')

    # prevent mouse events
    def OnMouseEvents(self, event):
        self.dataTerminal.SetFocus()
        return
        # if event.m_rightDown:
        #     event.Skip()

    # 保存数据
    def OnSaveData(self, event):
        self.dataTerminal.SaveFile(time.strftime('Terminal_%Y%m%d_%H_%M_%S.txt', time.localtime(time.time())))
        wx.MessageBox(u'保存成功')

    # 字符集更改
    def OnCharsetChange(self, event):
        self.ShowCharset = event.GetSelection()
        Config.APP_CONFIG['module_terminal']['charset'] = str(self.ShowCharset)

    # 新增串口数据，被串口接收所调用，这里的长度必须指定，否则字符串到\0将会终止
    def RecvData(self, data):
        self.DataWindowAppend(data)

    # 新增加数据到显示窗口，这里的长度必须指定，否则字符串到\0将会终止
    def DataWindowAppend(self, data):
        dataStr = []
        length = len(data)
        if self.ShowCharset == 0:
            # ASCII编码
            for i in range(0, length):
                d = data[i]
                if d == 0x0A or 32 <= d < 127:
                    dataStr.append(chr(d))

        elif self.ShowCharset == 1:
            # UTF8编码
            # 由于UTF8编码是变长编码，所以将数据先读入self.UnshowChars然后识别编码
            self.UnshowChars.extend(data)
            # 1字节 0xxxxxxx   
            # 2字节 110xxxxx 10xxxxxx   
            # 3字节 1110xxxx 10xxxxxx 10xxxxxx   
            # 4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   
            # 5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx   
            # 6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            oneChar = []
            while len(self.UnshowChars) > 0:
                firstChar = self.UnshowChars[0]

                del oneChar[:]

                if (0xFE & firstChar) == 0xFC:
                    if len(self.UnshowChars) >= 6:
                        for i in range(0, 6):
                            oneChar.append(chr(self.UnshowChars.pop(0)))
                    else:
                        break
                elif (0xFC & firstChar) == 0xF8:
                    if len(self.UnshowChars) >= 5:
                        for i in range(0, 5):
                            oneChar.append(chr(self.UnshowChars.pop(0)))
                    else:
                        break
                elif (0xF8 & firstChar) == 0xF0:
                    if len(self.UnshowChars) >= 4:
                        for i in range(0, 4):
                            oneChar.append(chr(self.UnshowChars.pop(0)))
                    else:
                        break
                elif (0xF0 & firstChar) == 0xE0:
                    if len(self.UnshowChars) >= 3:
                        for i in range(0, 3):
                            oneChar.append(chr(self.UnshowChars.pop(0)))
                    else:
                        break
                elif (0xE0 & firstChar) == 0xC0:
                    if len(self.UnshowChars) >= 2:
                        for i in range(0, 2):
                            oneChar.append(chr(self.UnshowChars.pop(0)))
                    else:
                        break
                elif (0x80 & firstChar) == 0x00:
                    if len(self.UnshowChars) >= 1:
                        for i in range(0, 1):
                            char = self.UnshowChars.pop(0)
                            if char == 0x0A or 32 <= char < 127:
                                oneChar.append(chr(char))
                    else:
                        break
                else:
                    self.UnshowChars.pop(0)

                if len(oneChar) > 0:
                    try:
                        dataStr.append(unicode(''.join(oneChar), 'utf-8'))
                    except:
                        pass
                else:
                    pass

        elif self.ShowCharset == 2:
            # GBK编码

            # 先存起来
            self.UnshowChars.extend(data)

            while len(self.UnshowChars) > 0:
                oneChar = []
                char = self.UnshowChars.pop(0)
                if char > 0x80:
                    if len(self.UnshowChars) < 2:
                        break
                    oneChar.append(chr(char))
                    char = self.UnshowChars[0]
                    oneChar.append(chr(char))
                    try:
                        wchar = (''.join(oneChar)).decode('GB2312', 'ignore')
                        dataStr.append(wchar)
                        self.UnshowChars.pop(0)
                    except:
                        dataStr.append('?')
                elif char == 0x0A or 32 <= char < 127:
                    dataStr.append(chr(char))

        if len(dataStr) > 0:
            wx.PostEvent(self, self.UpdateDataEvent(data=''.join(dataStr)))

    # 新增数据    
    def SaveUpdateWindow(self, evt):
        self.dataTerminal.SetInsertionPointEnd()
        self.dataTerminal.WriteText(evt.data)

    # 清空数据
    def OnClearData(self, event):
        self.dataTerminal.SetValue('')

    # 同时发送换行符
    def OnSendLineBreak(self, event):
        self.sendLineBreak = event.GetEventObject().GetValue()
        if self.sendLineBreak:
            Config.APP_CONFIG['module_terminal']['sendNewLine'] = '1'
        else:
            Config.APP_CONFIG['module_terminal']['sendNewLine'] = '0'

    # 保存设置
    def SaveUpdateWindow(self, evt):
        self.dataTerminal.SetInsertionPointEnd()
        self.dataTerminal.WriteText(evt.data)
